/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { KNativePointer } from "@koalaui/interop"
import { ETSModule, ImportDeclaration, isETSImportDeclaration, Program, Statement } from "../generated"
import { passNode, passNodeArray, unpackNonNullableNode } from "./utilities/private"
import { global } from "./static/global"
import { Es2pandaImportFlags, Es2pandaImportKinds } from "../generated/Es2pandaEnums"
import { factory } from "./factory/nodeFactory"

export class ImportStorage {
    // Improve: migrate to wrappers instead of pointers
    private imports: Set<KNativePointer> = new Set<KNativePointer>()
    private importSources: Set<string | undefined> = new Set<string | undefined>()

    constructor(private program: Program, private isParserStage: boolean) {
        for (const statement of program.ast.statements) {
            if (isETSImportDeclaration(statement)) {
                this.imports.add(statement.peer)
                if (!isParserStage) {
                    // Improve: is source non nullable?
                    this.importSources.add(statement.source?.str)
                }
            }
        }
    }

    update() {
        // Save current statements
        const statements = this.program.ast.statements

        // Update parser information
        const newStatements: Statement[] = []
        for (const statement of statements) {
            if (isETSImportDeclaration(statement) && !this.imports.has(statement.peer)) {
                if (!this.isParserStage && !this.importSources.has(statement.source?.str)) {
                    console.warn("Attempt to insert import from new source after parsed stage:")
                    console.warn(statement.dumpSrc())
                }

                const importDeclaration = unpackNonNullableNode<ImportDeclaration>(
                    // Note: this call is important, we cannot just pass "statement" to "InsertETSImportDeclarationAndParse"
                    global.es2panda._ETSParserBuildImportDeclaration(
                        global.context,
                        Es2pandaImportKinds.IMPORT_KINDS_ALL, // Improve: do we use IMPORT_KINDS_TYPES?
                        passNodeArray(statement.specifiers),
                        statement.specifiers.length,
                        passNode(statement.source),
                        this.program.peer,
                        Es2pandaImportFlags.IMPORT_FLAGS_NONE, // Improve: where to get it?
                    )
                )
                global.es2panda._InsertETSImportDeclarationAndParse(global.context, this.program.peer, importDeclaration.peer)
                newStatements.push(importDeclaration)
            } else {
                newStatements.push(statement)
            }
        }

        // Drop import statements generated by compiler in the beginning of the ETSModule
        const module = this.program.ast as ETSModule
        this.program.setAst(
            factory.updateETSModule(
                module,
                newStatements,
                module.ident,
                module.getNamespaceFlag(),
                module.program,
            )
        )
    }
}
